package com.uatair.common.tcp.builder;

import com.uatair.common.tcp.util.PositionData;
import com.uatair.common.tcp.util.ProtocolConstants;

import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

/***
 * 组装消息类
 */
public final class MessageBuilder {
    private static final int INITIAL_BUFFER_SIZE = 256;

    public static byte[] buildAuthMessage(String deviceId, long timestamp,
                                          short vendorId, String firmwareVer,
                                          String hardwareVer, short seq) {
        ByteBuffer body = ByteBuffer.allocate(INITIAL_BUFFER_SIZE)
                .order(ByteOrder.BIG_ENDIAN)
                .putLong(timestamp);
        body.put(calculateSignature(timestamp));
        body.putShort(vendorId);
        body.put(fixedLengthString(firmwareVer, 10));
        body.put(fixedLengthString(hardwareVer, 10));

        return buildMessage(ProtocolConstants.MSG_AUTH,
                Arrays.copyOf(body.array(), body.position()),
                deviceId, seq);
    }

    public static byte[] buildHeartbeatMessage(String deviceId, short seq) {
        return buildMessage(ProtocolConstants.MSG_HEARTBEAT, new byte[0], deviceId, seq);
    }

    public static byte[] buildPositionMessage(String deviceId, short seq, PositionData data) {
        ByteBuffer body = ByteBuffer.allocate(INITIAL_BUFFER_SIZE)
                .order(ByteOrder.BIG_ENDIAN)
                .putLong(data.timestamp)
                .put(data.positionStatus)
                .put(data.satelliteCount)
                .put((byte)(data.isEast ? 1 : 0))
                .putDouble(data.longitude)
                .put((byte)(data.isNorth ? 1 : 0))
                .putDouble(data.latitude)
                .putShort(data.altitude)
                .putShort(data.speed)
                .putShort(data.direction)
                .put(data.workStatus)
                .put(data.workType1).put(data.workType2)
                .put(data.workType3).put(data.workType4)
                .putShort(data.quality1).putShort(data.quality2).putShort(data.quality3)
                .put(fixedLengthString(data.droneModel, 20))
                .putShort(data.angleX)
                .putShort(data.angleY)
                .putShort(data.angleZ)
                .putShort(data.angle1X)
                .putShort(data.angle1Y)
                .putShort(data.angle1Z)
                .putShort(data.angle2X)
                .putShort(data.angle2Y)
                .putShort(data.angle2Z)
                .putShort(data.area)
                .put(data.signal)
                .put(fixedLengthString(data.workNum+"",4))
                .put(fixedLengthString(data.pictureNum+"",4));

        return buildMessage(ProtocolConstants.MSG_POSITION,
                Arrays.copyOf(body.array(), body.position()),
                deviceId, seq);
    }

    //7e 0011 03 00 0000008ea9fb9492 00 0100000197a09271bf040c01405c5fff887d019b730140413fff0e0dc6840096003b005a0133000000001e006400005131303
    // 00000000000000000000000000000000000000000000000000000000000000000000000000000000000 55 7e
    //7E 0011 03 00 0000008EA9FB9492 00 0000000197A10C48C5010901405A3479B0BB4942014041B00040014CDC002B0014012A0101010101000100010001303030303
    // 03030303631323733373139333130360001002E00520053001507140058003600160039002F010000000100010D 12 7E
    //7E 0001 03 00 7AEB 3631323733373139333130360000000000000000 0000 00000197C41E4ADA 3246374332433744373046413330333337373337433642343835384141443039 7AEB 56352E362E3500000000 48312E31332E39000000 88 7E

    //7E 8001 01 00 3631323733373139 33 31303620202020202020202020000100000197C409F9B80000 0001 02 92 7E
    //7E 8001 01 00 0000008EA9FB9492 00 0A 00000197A10FA9C8 0009 0011 02 13 7E
    //7E 8001 03 00 0000008EA9FB9492 00 09 00000197A112CAD3 0008 0002 00 67 7E
    private static byte[] buildMessage(short msgId, byte[] body, String deviceId, short seq) {
//        System.out.println("未组装body:" + new BigInteger(1, body).toString(16).toUpperCase());
        ByteBuffer buffer = ByteBuffer.allocate(ProtocolConstants.MAX_PACKET_SIZE)
                .order(ByteOrder.BIG_ENDIAN)
                .putShort(msgId)
                .put(ProtocolConstants.PROTOCOL_VERSION)
                .put((byte)0) // 无加密
                .putShort((short)31467) // 无加密
                .put(fixedLengthString(deviceId,20))
                .putShort(seq);
        byte[] header = Arrays.copyOf(buffer.array(), buffer.position());
        byte[] fullMessage = new byte[header.length + body.length];
        System.arraycopy(header, 0, fullMessage, 0, header.length);
        System.arraycopy(body, 0, fullMessage, header.length, body.length);
        byte checkCode = calculateCheckCode(fullMessage);
        return wrapMessage(fullMessage, checkCode);
    }

    private static byte calculateCheckCode(byte[] data) {
        byte check = 0;
        for (byte b : data)
            check ^= b;
        return check;
    }

    private static byte[] wrapMessage(byte[] data, byte checkCode) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        out.write(ProtocolConstants.START_FLAG);

        for (byte b : data) {
            writeEscapedByte(out, b);
        }
        writeEscapedByte(out, checkCode);

        out.write(ProtocolConstants.START_FLAG);
        return out.toByteArray();
    }

    private static void writeEscapedByte(ByteArrayOutputStream out, byte b) {
        if (b == ProtocolConstants.START_FLAG) {
            out.write(ProtocolConstants.ESCAPE_FLAG);
            out.write(0x02);
        } else if (b == ProtocolConstants.ESCAPE_FLAG) {
            out.write(ProtocolConstants.ESCAPE_FLAG);
            out.write(0x01);
        } else {
            out.write(b);
        }
    }

    private static byte[] calculateSignature(long timestamp) {
        try {
            String token = "4a8a7693796b8f7c47aed240354ce029";
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest((token + "|" + timestamp).getBytes());

            // MD5转32字节HEX
            byte[] hex = new byte[32];
            for (int i = 0; i < 16; i++) {
                hex[2*i] = (byte) Character.toUpperCase(Character.forDigit((digest[i] >> 4) & 0xF, 16));
                hex[2*i+1] = (byte) Character.toUpperCase(Character.forDigit(digest[i] & 0xF, 16));
            }
            return hex;
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("MD5 not available", e);
        }
    }

    private static byte[] fixedLengthString(String str, int length) {
        byte[] bytes = str.getBytes(StandardCharsets.US_ASCII);
        return Arrays.copyOf(bytes, length);
    }
}
